from typing import Dict, Optional, Union, List, TypedDict, Any

from django.db.models.query import QuerySet
from django.utils.timezone import now

from product import models
import product.utils.order as order
import product.utils.work_with_stripe as work_with_stripe


class TaxRateType(TypedDict):
    """ Тип налоговой ставки """

    display_name: str
    percentage: Union[int, float]


class OrderUtils:
    """ Инструменты для обработки запросов связанных с заказом """

    def get_last_order(self) -> models.Order:
        """ Получаем последний, не оплаченный заказ """

        number_of_unpaid_orders = len(
            models.Order.objects.filter(paid_for=False)
        )
        if number_of_unpaid_orders == 0:
            order = models.Order.objects.create()
        elif number_of_unpaid_orders == 1:
            order = models.Order.objects.get(paid_for=False)
        else:
            message = 'Активных заказов больше одного'
            raise ValueError(message)
        return order


    def make_order_complete(
            self,
            order: models.Order,
            list_of_products: QuerySet) -> Any:
        """ Выполняем завершение заказа """

        [order.product_set.add(product) for product in list_of_products]

        order.total_price_of_products = sum([product['price'] \
         for product in list_of_products.values('price')])

        order.paid_for = True
        order.date_of_placing_order = now().isoformat()
        order.save()


    def check_availability_of_product_in_order(
            self,
            order: models.Order,
            product: models.Product) -> bool:
        """ Проверяем наличие продукта в заказе """

        return product in order.product_set.all()


    def add_product_to_order(
            self,
            order: models.Order,
            product: models.Product) -> Dict[str, str]:
        """ Добавляем продукт в заказ """

        if self.check_availability_of_product_in_order(order, product):
            response = {
                'status': 'failure',
                'message': 'Продукт уже добавлен в корзину'
            }
        else:
            product.order = order
            product.save()
            response = {
                'status': 'success',
                'message': 'Продукт добавлен в корзину'
            }

        return response


    def get_order_products(self, type_of_dict=True) -> QuerySet:
        """ Получаем продукты заказа """

        order = self.get_last_order()

        if type_of_dict:
            order_products = order.product_set.all().values('name', 'price')
        else:
            order_products = order.product_set.all()

        return order_products


    def get_order_amount(self) -> int:
        """ Получаем сумму заказа """

        order = self.get_last_order()
        list_of_prices = [int(product.price) for product in order.product_set.all()]
        order_amount = sum(list_of_prices)
        return order_amount


    def get_quantity_of_order_products(self) -> int:
        """ Получаем количество продуктов заказа """

        order = self.get_last_order()
        return len(order.product_set.all())


    def add_discount_to_order(self,
              order: models.Order,
              discount: int) -> None:
        """ Добавляем скидку заказу """

        discount_object, _ = models.Discount.objects.get_or_create(
            percent_off=discount
        )
        order.discount = discount_object
        order.save()


    def get_discount_for_paid_session(
            self,
            discount: Optional[int]) -> Union[List, List[Dict['str', 'str']]]:
        """ Получаем и устанавливаем скидку для оплачиваемой сессии """

        if not discount: return []

        self.add_discount_to_order(
            order=self.get_last_order(),
            discount=discount
        )
        coupon_id = work_with_stripe.DiscountApiStripe().get_or_create_coupon(discount)
        return [{
            'coupon': coupon_id,
        }]


    def add_tax_rates_to_order(
        self,
        order: models.Order,
        tax_rates: int) -> None:
        """ Устанавливаем налоговую ставку для заказа """

        for tax_rate in tax_rates:
            tax_rate_object, _ = models.TaxRate.objects.get_or_create(
                name=tax_rate['display_name'],
                precentage=tax_rate['percentage']
            )
            order.tax_rates.add(tax_rate_object)
            order.save()


    def set_tax_rate_for_order(
            self,
            tax_rates: Optional[List[TaxRateType]],
            content_of_line_items: List[Dict[str, Union[str, int]]]
            ) -> None:
        """ Устанавливаем налоговую ставку для заказа """

        if not tax_rates: return

        tax_rates_ids = []
        self.add_tax_rates_to_order(
            order=self.get_last_order(),
            tax_rates=tax_rates
        )

        for tax_rate in tax_rates:
            tax_rate_id = work_with_stripe.TaxRateApiStripe() \
            .get_or_create_tax_rate(
                display_name=tax_rate['display_name'],
                percentage=tax_rate['percentage']
            )
            tax_rates_ids.append(tax_rate_id)

        for product in content_of_line_items:
            product['tax_rates'] = tax_rates_ids


    def add_additional_options_to_order(
            self,
            content_of_line_items: List[Dict[str, Any]],
            discount: Optional[int] = None,
            tax_rates: Optional[List[order.TaxRateType]] = None) -> Dict[str, Any]:
        """ Добавляем дополнительные опции в заказ если это требуется """

        data = {}
        order.OrderUtils().set_tax_rate_for_order(
            tax_rates,
            content_of_line_items
        )

        discount_on_order = order.OrderUtils().get_discount_for_paid_session(
            discount
        )
        data['discount_on_order'] = discount_on_order
        return data


    def add_product_to_order_payment_session(
        self,
        product: Optional[models.Product] = None,
        list_of_products: Optional[QuerySet] = None) -> Any:
        """ Добавляем продукт в сессию оплаты заказа """

        def get_contents_of_line_items_for_products(
                list_of_products: QuerySet) -> List[Dict[str, str]]:
            """ Получаем line_items списка продуктов """

            list_of_prices_id = work_with_stripe.WorkWithApiStripe().get_price_id_list_from_list_of_products(
                list_of_products
            )
            return [{'price': price, 'quantity': 1} \
                    for price in list_of_prices_id]


        def get_content_of_line_items_for_product(
                product: models.Product) -> List[Dict[str, Union[str, int]]]:
            """ Получаем содержимое line_items для одного продукта """

            price_id = work_with_stripe.WorkWithApiStripe().create_price_by_product_object(
                product
            )
            content_of_line_items = [{
                'price': price_id,
                'quantity': 1,
            }]
            return content_of_line_items


        # ВЫШЕ ОПРЕДЕЛЕНИЕ ФУНКЦИЙ

        if list_of_products:
            return get_contents_of_line_items_for_products(
                list_of_products
            )
        else:
            return get_content_of_line_items_for_product(
                product
            )

